/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {defineComponent, onMounted, PropType, ref, watch} from 'vue'
import {NInputNumber, NRadio, NRadioGroup, NSelect} from 'naive-ui'
import {useI18n} from 'vue-i18n'
import {
    isStr,
    isWeek,
    week,
    specificWeek,
    lastWeeks,
    specificList
} from '../common'
import styles from '../index.module.scss'

const props = {
    dayValue: {
        type: String as PropType<string>,
        default: '*'
    },
    weekValue: {
        type: String as PropType<string>,
        default: '?'
    }
}

export default defineComponent({
    name: 'CrontabDay',
    props,
    emits: ['update:dayValue', 'update:weekValue'],
    setup(props, ctx) {
        const {t} = useI18n()

        const weekOptions = week.map((v) => ({
            label: t(v.label),
            value: v.value
        }))

        const lastWeekOptions = lastWeeks.map((v) => ({
            label: t(v.label),
            value: v.value
        }))

        const radioRef = ref()
        const dayRef = ref()
        const weekRef = ref()
        const WkintervalWeekStartRef = ref(2)
        const WkintervalWeekPerformRef = ref(2)
        const intervalDayStartRef = ref(1)
        const intervalDayPerformRef = ref(1)
        const WkspecificDayRef = ref<Array<number>>([])
        const WkspecificWeekRef = ref<Array<string>>([])
        const monthLastDaysRef = ref('L')
        const monthLastWorkingDaysRef = ref('LW')
        const monthLastWeeksRef = ref('?')
        const monthTailBeforeRef = ref(1)
        const recentlyWorkingDaysMonthRef = ref(1)
        const WkmonthNumWeeksDayRef = ref(1)
        const WkmonthNumWeeksWeekRef = ref(1)

        /**
         * Parse parameter value
         */
        const analyticalValue = () => {
            const $dayVal = props.dayValue
            const $weekVal = props.weekValue
            const isWeek1 = $weekVal.indexOf('/') !== -1
            const isWeek2 = $weekVal.indexOf('#') !== -1

            // Initialization
            if ($dayVal === '*' && $weekVal === '?') {
                radioRef.value = 'everyDay'
                return
            }

            // week
            if (isWeek1 || isWeek2 || isWeek($weekVal)) {
                dayRef.value = '?'

                /**
                 * Processing by sequence number (excluding days)
                 * @param [
                 * WkintervalWeek=>(/),
                 * WkspecificWeek=>(TUE,WED),
                 * WkmonthNumWeeks=>(#)
                 * ]
                 */
                const hanleWeekOne = () => {
                    const a = isStr($weekVal, '/') as string[]
                    WkintervalWeekStartRef.value = parseInt(a[0])
                    WkintervalWeekPerformRef.value = parseInt(a[1])
                    dayRef.value = '?'
                    weekRef.value = `${WkintervalWeekPerformRef.value}/${WkintervalWeekStartRef.value}`
                    radioRef.value = 'WkintervalWeek'
                }

                const hanleWeekTwo = () => {
                    WkspecificWeekRef.value = $weekVal.split(',')
                    radioRef.value = 'WkspecificWeek'
                }

                const hanleWeekThree = () => {
                    const a = isStr($weekVal, '#') as string[]
                    WkmonthNumWeeksDayRef.value = parseInt(a[0])
                    WkmonthNumWeeksDayRef.value = parseInt(a[1])
                    radioRef.value = 'WkmonthNumWeeks'
                }

                // Processing week
                if (isStr($weekVal, '/')) {
                    hanleWeekOne()
                } else if (isStr($weekVal, '#')) {
                    hanleWeekThree()
                } else if (isWeek($weekVal)) {
                    hanleWeekTwo()
                }
            } else {
                weekRef.value = '?'

                /**
                 * Processing by sequence number (excluding week)
                 * @param [
                 * everyDay=>(*),
                 * intervalDay=>(1/1),
                 * specificDay=>(1,2,5,3,4),
                 * monthLastDays=>(L),
                 * monthLastWorkingDays=>(LW),
                 * monthLastWeeks=>(3L),
                 * monthTailBefore=>(L-4),
                 * recentlyWorkingDaysMonth=>(6W)
                 * ]
                 */
                const hanleDayOne = () => {
                    radioRef.value = 'everyDay'
                }

                const hanleDayTwo = () => {
                    const a = isStr($dayVal, '/') as string[]
                    intervalDayStartRef.value = parseInt(a[0])
                    intervalDayPerformRef.value = parseInt(a[1])
                    radioRef.value = 'intervalDay'
                }

                const hanleDayThree = () => {
                    WkspecificDayRef.value = $dayVal
                        .split(',')
                        .map((item) => parseInt(item))
                    radioRef.value = 'specificDay'
                }

                const hanleDayFour = () => {
                    radioRef.value = 'monthLastDays'
                }

                const hanleDayFive = () => {
                    radioRef.value = 'monthLastWorkingDays'
                }

                const hanleDaySix = () => {
                    monthLastWeeksRef.value = $dayVal
                    radioRef.value = 'monthLastWeeks'
                }

                const hanleDaySeven = () => {
                    const a = isStr($dayVal, '-') as string[]
                    monthTailBeforeRef.value = parseInt(a[1])
                    radioRef.value = 'monthTailBefore'
                }

                const hanleDayEight = () => {
                    recentlyWorkingDaysMonthRef.value = parseInt(
                        $dayVal.slice(0, $dayVal.length - 1)
                    )
                    radioRef.value = 'recentlyWorkingDaysMonth'
                }

                if ($dayVal === '*') {
                    hanleDayOne()
                } else if (isStr($dayVal, '/')) {
                    hanleDayTwo()
                } else if ($dayVal === 'L') {
                    hanleDayFour()
                } else if ($dayVal === 'LW') {
                    hanleDayFive()
                } else if ($dayVal.charAt($dayVal.length - 1) === 'L') {
                    hanleDaySix()
                } else if (isStr($dayVal, '-')) {
                    hanleDaySeven()
                } else if ($dayVal.charAt($dayVal.length - 1) === 'W') {
                    hanleDayEight()
                } else {
                    hanleDayThree()
                }
            }
        }

        // Every few weeks
        const onWkintervalWeekPerform = (value: number | null) => {
            WkintervalWeekPerformRef.value = value || 0
            if (radioRef.value === 'WkintervalWeek') {
                dayRef.value = '?'
                weekRef.value = `${WkintervalWeekStartRef.value}/${WkintervalWeekPerformRef.value}`
            }
        }

        // Every few weeks
        const onWkintervalWeekStart = (value: number | null) => {
            WkintervalWeekStartRef.value = value || 0
            if (radioRef.value === 'WkintervalWeek') {
                dayRef.value = '?'
                weekRef.value = `${WkintervalWeekStartRef.value}/${WkintervalWeekPerformRef.value}`
            }
        }

        // Interval start time（1）
        const onIntervalDayStart = (value: number | null) => {
            intervalDayStartRef.value = value || 0
            if (radioRef.value === 'intervalDay') {
                intervalDaySet()
            }
        }

        // Interval execution time（2）
        const onIntervalDayPerform = (value: number | null) => {
            intervalDayPerformRef.value = value || 0
            if (radioRef.value === 'intervalDay') {
                intervalDaySet()
            }
        }

        // Specific day of the week (multiple choice)
        const onWkspecificWeek = (arr: Array<string>) => {
            WkspecificWeekRef.value = arr
            if (radioRef.value === 'WkspecificWeek') {
                dayRef.value = '?'
                weekRef.value = arr.join(',')
            }
        }

        // Specific days (multiple choices)
        const onWkspecificDay = (arr: Array<number>) => {
            WkspecificDayRef.value = arr
            if (radioRef.value === 'specificDay') {
                weekRef.value = '?'
                dayRef.value = arr.join(',')
            }
        }

        const onMonthLastWeeks = (value: string | null) => {
            monthLastWeeksRef.value = value || '?'
            if (radioRef.value === 'monthLastWeeks') {
                weekRef.value = value
                dayRef.value = '?'
            }
        }

        // Specific days
        const onSpecificDays = (arr: Array<number>) => {
            WkspecificDayRef.value = arr
            if (radioRef.value === 'specificDay') {
                specificSet()
            }
        }

        // By the end of this month
        const onMonthTailBefore = (value: number | null) => {
            monthTailBeforeRef.value = value || 0
            if (radioRef.value === 'monthTailBefore') {
                dayRef.value = `L-${monthTailBeforeRef.value}`
            }
        }

        // Last working day
        const onRecentlyWorkingDaysMonth = (value: number | null) => {
            recentlyWorkingDaysMonthRef.value = value || 0
            if (radioRef.value === 'recentlyWorkingDaysMonth') {
                dayRef.value = `${recentlyWorkingDaysMonthRef.value}W`
            }
        }

        // On the day of this month
        const onWkmonthNumWeeksDay = (value: number | null) => {
            WkmonthNumWeeksDayRef.value = value || 0
            if (radioRef.value === 'WkmonthNumWeeks') {
                weekRef.value = `${WkmonthNumWeeksWeekRef.value}#${WkmonthNumWeeksDayRef.value}`
            }
        }

        // On the week of this month
        const onWkmonthNumWeeksWeek = (value: number | null) => {
            if (radioRef.value === 'WkmonthNumWeeks') {
                dayRef.value = '?'
                weekRef.value = `${value}#${WkmonthNumWeeksDayRef.value}`
            }
        }

        // Reset every day
        const everyDaySet = () => {
            dayRef.value = '*'
        }

        // Reset interval week starts from *
        const WkintervalWeekReset = () => {
            weekRef.value = `${WkintervalWeekStartRef.value}/${WkintervalWeekPerformRef.value}`
        }

        // Reset interval days
        const intervalDaySet = () => {
            dayRef.value = `${intervalDayStartRef.value}/${intervalDayPerformRef.value}`
        }

        // Specific week (multiple choices)
        const WkspecificWeekReset = () => {
            weekRef.value = WkspecificWeekRef.value.length
                ? WkspecificWeekRef.value.join(',')
                : '*'
        }

        // Reset specific days
        const specificSet = () => {
            if (WkspecificDayRef.value.length) {
                dayRef.value = WkspecificDayRef.value.join(',')
            } else {
                dayRef.value = '*'
            }
        }

        // On the last day of the month
        const monthLastDaysReset = () => {
            dayRef.value = monthLastDaysRef.value
        }

        // On the last working day of the month
        const monthLastWorkingDaysReset = () => {
            dayRef.value = monthLastWorkingDaysRef.value
        }

        // At the end of the month*
        const monthLastWeeksReset = () => {
            dayRef.value = monthLastWeeksRef.value
        }

        // By the end of this month
        const monthTailBeforeReset = () => {
            dayRef.value = `L-${monthTailBeforeRef.value}`
        }

        // Last working day (Monday to Friday) to this month
        const recentlyWorkingDaysMonthReset = () => {
            dayRef.value = `${recentlyWorkingDaysMonthRef.value}W`
        }

        // On the day of this month
        const WkmonthNumReset = () => {
            weekRef.value = `${WkmonthNumWeeksWeekRef.value}#${WkmonthNumWeeksDayRef.value}`
        }

        const updateRadioDay = (value: string) => {
            switch (value) {
                case 'everyDay':
                    weekRef.value = '?'
                    everyDaySet()
                    break
                case 'WkintervalWeek':
                    dayRef.value = '?'
                    WkintervalWeekReset()
                    break
                case 'intervalDay':
                    weekRef.value = '?'
                    intervalDaySet()
                    break
                case 'WkspecificWeek':
                    dayRef.value = '?'
                    WkspecificWeekReset()
                    break
                case 'specificDay':
                    weekRef.value = '?'
                    specificSet()
                    break
                case 'monthLastDays':
                    weekRef.value = '?'
                    monthLastDaysReset()
                    break
                case 'monthLastWorkingDays':
                    weekRef.value = '?'
                    monthLastWorkingDaysReset()
                    break
                case 'monthLastWeeks':
                    weekRef.value = '1L'
                    monthLastWeeksReset()
                    break
                case 'monthTailBefore':
                    weekRef.value = '?'
                    monthTailBeforeReset()
                    break
                case 'recentlyWorkingDaysMonth':
                    weekRef.value = '?'
                    recentlyWorkingDaysMonthReset()
                    break
                case 'WkmonthNumWeeks':
                    dayRef.value = '?'
                    WkmonthNumReset()
                    break
            }
        }

        watch(
            () => dayRef.value,
            () => ctx.emit('update:dayValue', dayRef.value.toString())
        )

        watch(
            () => weekRef.value,
            () => ctx.emit('update:weekValue', weekRef.value.toString())
        )

        onMounted(() => analyticalValue())

        return {
            weekOptions,
            lastWeekOptions,
            radioRef,
            WkintervalWeekStartRef,
            WkintervalWeekPerformRef,
            intervalDayStartRef,
            intervalDayPerformRef,
            WkspecificWeekRef,
            WkspecificDayRef,
            monthLastWeeksRef,
            monthTailBeforeRef,
            recentlyWorkingDaysMonthRef,
            WkmonthNumWeeksDayRef,
            WkmonthNumWeeksWeekRef,
            updateRadioDay,
            onWkintervalWeekStart,
            onWkintervalWeekPerform,
            onIntervalDayStart,
            onIntervalDayPerform,
            onSpecificDays,
            onWkspecificWeek,
            onWkspecificDay,
            onMonthLastWeeks,
            onMonthTailBefore,
            onRecentlyWorkingDaysMonth,
            onWkmonthNumWeeksDay,
            onWkmonthNumWeeksWeek
        }
    },
    render() {
        const {t} = useI18n()

        return (
            <NRadioGroup
                v-model:value={this.radioRef}
                onUpdateValue={this.updateRadioDay}
            >
                <div class={styles['crontab-list']}>
                    <NRadio value={'everyDay'}/>
                    <div class={styles['crontab-list-item']}>
                        <div>{t('crontab.every_day')}</div>
                    </div>
                </div>
                <div class={styles['crontab-list']}>
                    <NRadio value={'WkintervalWeek'}/>
                    <div class={styles['crontab-list-item']}>
                        <div class={styles['item-text']}>{t('crontab.every')}</div>
                        <div class={styles['number-input']}>
                            <NInputNumber
                                defaultValue={0}
                                min={0}
                                max={7}
                                v-model:value={this.WkintervalWeekPerformRef}
                                onUpdateValue={this.onWkintervalWeekPerform}
                            />
                        </div>
                        <div class={styles['item-text']}>
                            {t('crontab.day_carried_out')}
                        </div>
                        <div>
                            <NSelect
                                options={this.weekOptions}
                                defaultValue={this.WkintervalWeekStartRef}
                                v-model:value={this.WkintervalWeekStartRef}
                                onUpdateValue={this.onWkintervalWeekStart}
                            />
                        </div>
                        <div>{t('crontab.start')}</div>
                    </div>
                </div>
                <div class={styles['crontab-list']}>
                    <NRadio value={'intervalDay'}/>
                    <div class={styles['crontab-list-item']}>
                        <div>{t('crontab.every')}</div>
                        <div class={styles['number-input']}>
                            <NInputNumber
                                defaultValue={0}
                                min={0}
                                max={31}
                                v-model:value={this.intervalDayPerformRef}
                                onUpdateValue={this.onIntervalDayPerform}
                            />
                        </div>
                        <div>{t('crontab.day_carried_out')}</div>
                        <div class={styles['number-input']}>
                            <NInputNumber
                                defaultValue={0}
                                min={1}
                                max={31}
                                v-model:value={this.intervalDayStartRef}
                                onUpdateValue={this.onIntervalDayStart}
                            />
                        </div>
                        <div>{t('crontab.day_start')}</div>
                    </div>
                </div>
                <div class={styles['crontab-list']}>
                    <NRadio value={'WkspecificWeek'}/>
                    <div class={styles['crontab-list-item']}>
                        <div>{t('crontab.specific_week')}</div>
                        <div>
                            <NSelect
                                style={{width: '300px'}}
                                multiple
                                options={specificWeek}
                                placeholder={t('crontab.specific_week_tip')}
                                v-model:value={this.WkspecificWeekRef}
                                onUpdateValue={this.onWkspecificWeek}
                            />
                        </div>
                    </div>
                </div>
                <div class={styles['crontab-list']}>
                    <NRadio value={'specificDay'}/>
                    <div class={styles['crontab-list-item']}>
                        <div>{t('crontab.specific_day')}</div>
                        <div>
                            <NSelect
                                style={{width: '300px'}}
                                multiple
                                options={specificList.day}
                                placeholder={t('crontab.specific_day_tip')}
                                v-model:value={this.WkspecificDayRef}
                                onUpdateValue={this.onWkspecificDay}
                            />
                        </div>
                    </div>
                </div>
                <div class={styles['crontab-list']}>
                    <NRadio value={'monthLastDays'}/>
                    <div class={styles['crontab-list-item']}>
                        <div>{t('crontab.last_day_of_month')}</div>
                    </div>
                </div>
                <div class={styles['crontab-list']}>
                    <NRadio value={'monthLastWorkingDays'}/>
                    <div class={styles['crontab-list-item']}>
                        <div>{t('crontab.last_work_day_of_month')}</div>
                    </div>
                </div>
                <div class={styles['crontab-list']}>
                    <NRadio value={'monthLastWeeks'}/>
                    <div class={styles['crontab-list-item']}>
                        <div>{t('crontab.last_of_month')}</div>
                        <div>
                            <NSelect
                                style={{width: '150px'}}
                                options={this.lastWeekOptions}
                                defaultValue={this.monthLastWeeksRef}
                                v-model:value={this.monthLastWeeksRef}
                                onUpdateValue={this.onMonthLastWeeks}
                            />
                        </div>
                    </div>
                </div>
                <div class={styles['crontab-list']}>
                    <NRadio value={'monthTailBefore'}/>
                    <div class={styles['crontab-list-item']}>
                        <div class={styles['number-input']}>
                            <NInputNumber
                                defaultValue={0}
                                min={0}
                                max={31}
                                v-model:value={this.monthTailBeforeRef}
                                onUpdateValue={this.onMonthTailBefore}
                            />
                        </div>
                        <div>{t('crontab.before_end_of_month')}</div>
                    </div>
                </div>
                <div class={styles['crontab-list']}>
                    <NRadio value={'recentlyWorkingDaysMonth'}/>
                    <div class={styles['crontab-list-item']}>
                        <div>{t('crontab.recent_business_day_to_month')}</div>
                        <div class={styles['number-input']}>
                            <NInputNumber
                                style={{width: '100px'}}
                                defaultValue={0}
                                min={0}
                                max={31}
                                v-model:value={this.recentlyWorkingDaysMonthRef}
                                onUpdateValue={this.onRecentlyWorkingDaysMonth}
                            />
                        </div>
                        <div style={{width: '50px'}}>{t('crontab.one_day')}</div>
                    </div>
                </div>
                <div class={styles['crontab-list']}>
                    <NRadio value={'WkmonthNumWeeks'}/>
                    <div class={styles['crontab-list-item']}>
                        <div>{t('crontab.in_this_months')}</div>
                        <div class={styles['number-input']}>
                            <NInputNumber
                                defaultValue={0}
                                min={0}
                                max={31}
                                v-model:value={this.WkmonthNumWeeksDayRef}
                                onUpdateValue={this.onWkmonthNumWeeksDay}
                            />
                        </div>
                        <div>
                            <NSelect
                                style={{width: '150px'}}
                                options={this.weekOptions}
                                defaultValue={this.WkmonthNumWeeksWeekRef}
                                v-model:value={this.WkmonthNumWeeksWeekRef}
                                onUpdateValue={this.onWkmonthNumWeeksWeek}
                            />
                        </div>
                    </div>
                </div>
            </NRadioGroup>
        )
    }
})
